package com.market.domain;

import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.faces.context.FacesContext;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import com.market.domain.transients.Email;
import com.market.domain.transients.MarketDate;
import com.market.util.StringUtil;

@Entity
@Table(name = "orders")
public class Order extends BaseObject {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6820416036587674965L;

	public enum STATUS {
		UNKNOWN, RECEIVED, CONFIRMED, PREPARED, DELIVERED, PICKED_UP, PAID, CANCELED;
		private String getMessage(String key) {
			Locale locale = FacesContext.getCurrentInstance().getViewRoot()
					.getLocale();
			java.util.ResourceBundle resBundle = ResourceBundle.getBundle(
					"com.market.messages", locale);
			return resBundle.getString(key);
		}

		private String getMessage(String key, Locale locale) {
			java.util.ResourceBundle resBundle = ResourceBundle.getBundle(
					"com.market.messages", locale);
			return resBundle.getString(key);
		}

		public String getLabel() {
			return this.getMessage("ORDER_STATUS_" + this.toString());

		}

		public String getLabel(Locale locale) {
			return this.getMessage("ORDER_STATUS_" + this.toString(), locale);
		}
	};

	/*
	 * private String formatCentString(String s){ String result = ""; String
	 * cent = s.substring(s.length() -1);
	 * 
	 * 
	 * 
	 * if(cent.equals("8") || cent.equals("9")){ cent = "0"; result =
	 * s.substring(s.length() - 2, s.length() - 1); Integer tenCent =
	 * Integer.parseInt(result) + 1; result = s.substring(0, s.length() - 2) +
	 * tenCent.toString() + cent; }else{ if(cent.equals("1") ||
	 * cent.equals("2")){ cent = "0"; } if(cent.equals("3") ||
	 * cent.equals("4")){ cent = "5"; } if(cent.equals("6") ||
	 * cent.equals("7")){ cent = "5"; } result = s.substring(0, s.length() -1) +
	 * cent; } return result; }
	 */

	@Override
	public boolean equalsId(Object o) {
		if ((o instanceof Order) && (((Order) o).getId().equals(this.id))) {
			return true;
		}
		return false;
	}

	@Transient
	private Boolean isAcceptDeliveryFee = Boolean.FALSE;

	public Boolean getIsAcceptDeliveryFee() {
		return isAcceptDeliveryFee;
	}

	public void setIsAcceptDeliveryFee(Boolean isAcceptDeliveryFee) {
		this.isAcceptDeliveryFee = isAcceptDeliveryFee;
	}

	public Boolean getIsCanCalculateExactAmount() {
		for (OrderDetail detail : this.orderDetails) {
			if (detail.getProduct().getIsCanCaculateExactAmount()) {
				return Boolean.TRUE;
			}
		}
		return Boolean.FALSE;
	}

	/**
	 * Copy ordered price and ordered quantity to confirmed price and delivered
	 * quantity.if confirmedQuanity is not zero, means this item has been
	 * confirmed manually.
	 */

	public void orderedToConfirmed() {
		for (OrderDetail detail : this.orderDetails) {
			detail.setConfirmedQuantity(detail.getOrderedQuantity());
			detail.setConfirmedPrice(detail.getOrderedPrice());
		}
	}

	/**
	 * This method copy confirmedQuantity and confirmedPrice to
	 * deliveredQuantity and deliveredPrice. when set deliveredQuantity,
	 * confirmedQuantity have to * specification. because confirmedQuantity use
	 * stockUnit, but deliveredQuantity use priceUnit.
	 */

	public void confirmedToDelivered() {
		for (OrderDetail detail : this.orderDetails) {
			// detail.setConfirmedQuantityToDeliveredQuantity(detail
			// .getConfirmedQuantity());
			detail.setDeliveredQuantity(detail.getConfirmedQuantity()
					* detail.getProduct().getSpecification());
			detail.setDeliveredPrice(detail.getConfirmedPrice());
		}
	}

	@Transient
	public Integer getOrderedQuantity() {
		Integer quantity = 0;
		for (OrderDetail detail : this.orderDetails) {
			quantity = quantity + detail.getOrderedQuantity();
		}
		return quantity;
	}

	public Integer getConfirmedQuantity() {
		Integer quantity = 0;
		for (OrderDetail detail : this.orderDetails) {
			quantity = quantity + detail.getConfirmedQuantity();
		}
		return quantity;
	}

	@Transient
	public Double getDeliveredQuantity() {
		Double quantity = 0D;
		for (OrderDetail detail : this.orderDetails) {
			quantity = quantity + detail.getDeliveredQuantity();
		}
		return quantity;
	}

	public String getDeliveredQuantityLabel() {
		DecimalFormat df = new DecimalFormat("#.000");
		Double deliveredQuantity = this.getDeliveredQuantity();
		return df.format(deliveredQuantity);
	}

	public String getRequiredDateLabel() {
		SimpleDateFormat formate = new SimpleDateFormat("yyyy-MM-dd");
		String label = formate.format(this.requiredDate);
		MarketDate marketDate = new MarketDate(this.requiredDate);
		label = label + " " + marketDate.getDayOfWeekLabel();
		return label;
	}

	@Transient
	public Double getOrderedTotalAmount() {
		Double total = 0D;
		for (OrderDetail detail : this.orderDetails) {
			total = total + detail.getOrderedSubTotal();
		}
		return total;
	}

	@Transient
	public String getOrderedTotalAmountLabel() {
		Double total = this.getOrderedTotalAmount();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getOrderedTotalPlusTax() {
		return this.getOrderedTotalAmount() + this.getOrderedTotalGST()
				+ this.getOrderedTotalQST();
	}

	@Transient
	public String getOrderedTotalPlusTaxLabel() {
		Double total = this.getOrderedTotalPlusTax();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getOrderedTotalGST() {
		Double total = 0D;
		for (OrderDetail detail : this.orderDetails) {
			total = total + detail.getOrderedGST();
		}
		return total;
	}

	@Transient
	public String getOrderedTotalGSTLabel() {
		Double total = this.getOrderedTotalGST();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getOrderedTotalQST() {
		Double total = 0D;
		for (OrderDetail detail : this.orderDetails) {
			total = total + detail.getOrderedQST();
		}
		return total;
	}

	@Transient
	public String getOrderedTotalQSTLabel() {
		Double total = this.getOrderedTotalQST();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getOrderedTotalCharge() {
		Double total = this.getOrderedTotalPlusTax() + this.getDeliveryFee()
				- this.getPaidByCreditAmount();
		return total;
	}

	@Transient
	public String getOrderedTotalChargeLabel() {
		Double total = this.getOrderedTotalCharge();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getConfirmedTotalAmount() {
		Double total = 0D;
		for (OrderDetail detail : this.orderDetails) {
			total = total + detail.getConfirmedSubTotal();
		}
		return total;
	}

	@Transient
	public String getConfirmedTotalAmountLabel() {
		Double total = this.getConfirmedTotalAmount();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getConfirmedTotalCharge() {
		Double total = this.getConfirmedTotalPlusTax() + this.getDeliveryFee()
				- this.getPaidByCreditAmount();
		return total;
	}

	@Transient
	public String getConfirmedTotalChargeLabel() {
		Double total = this.getConfirmedTotalCharge();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getConfirmedTotalPlusTax() {
		return this.getConfirmedTotalAmount() + this.getDeliveredTotalGST()
				+ this.getDeliveredTotalQST();
	}

	@Transient
	public String getConfirmedTotalPlusTaxLabel() {
		Double total = this.getConfirmedTotalPlusTax();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getConfirmedTotalGST() {
		Double total = 0D;
		for (OrderDetail detail : this.orderDetails) {
			total = total + detail.getConfirmedGST();
		}
		return total;
	}

	@Transient
	public String getConfirmedTotalGSTLabel() {
		Double total = this.getConfirmedTotalGST();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getConfirmedTotalQST() {
		Double total = 0D;
		for (OrderDetail detail : this.orderDetails) {
			total = total + detail.getConfirmedQST();
		}
		return total;
	}

	@Transient
	public String getConfirmedTotalQSTLabel() {
		Double total = this.getConfirmedTotalQST();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getDeliveredTotalAmount() {
		Double total = 0D;
		for (OrderDetail detail : this.orderDetails) {
			total = total + detail.getDeliveredSubTotal();
		}
		return total;
	}

	@Transient
	public String getDeliveredTotalAmountLabel() {
		Double total = this.getDeliveredTotalAmount();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getDeliveredTotalPlusTax() {
		return this.getDeliveredTotalAmount() + this.getDeliveredTotalGST()
				+ this.getDeliveredTotalQST();
	}

	@Transient
	public String getDeliveredTotalPlusTaxLabel() {
		Double total = this.getDeliveredTotalPlusTax();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getDeliveredTotalGST() {
		Double total = 0D;
		for (OrderDetail detail : this.orderDetails) {
			total = total + detail.getDeliveredGST();
		}
		return total;
	}

	@Transient
	public String getDeliveredTotalGSTLabel() {
		Double total = this.getDeliveredTotalGST();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getDeliveredTotalQST() {
		Double total = 0D;
		for (OrderDetail detail : this.orderDetails) {
			total = total + detail.getDeliveredQST();
		}
		return total;
	}

	@Transient
	public String getDeliveredTotalQSTLabel() {
		Double total = this.getDeliveredTotalQST();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getDeliveredTotalChargeExcludeTax() {
		Double total = this.getDeliveredTotalAmount() + this.getDeliveryFee()
				- this.getPaidByCreditAmount();
		return total;
	}

	@Transient
	public String getDeliveredTotalChargeExcludeTaxLabel() {
		Double total = this.getDeliveredTotalChargeExcludeTax();
		return super.formatDoubleToString(total);
	}

	@Transient
	public Double getDeliveredTotalCharge() {
		Double total = this.getDeliveredTotalPlusTax() + this.getDeliveryFee()
				- this.getPaidByCreditAmount();
		return total;
	}

	@Transient
	public String getDeliveredTotalChargeLabel() {
		Double total = this.getDeliveredTotalCharge();
		return super.formatDoubleToString(total);
	}

	public Double getPromotionRewardAmount() {
		Integer rate = this.getPromotionRewardRate();
		Double rewardAmount = this.getDeliveredTotalAmount() * rate / 100D;
		return rewardAmount;
	}

	public String getPromotionRewardAmountLabel() {
		Double amount = this.getPromotionRewardAmount();
		return super.formatDoubleToString(amount);
	}

	@Transient
	public String getDiscountAmountLabel() {
		Double total = this.getDiscountAmount();
		return super.formatDoubleToString(total);
	}

	@Transient
	public String getPaidAmountLabel() {
		Double total = this.getPaidAmount();
		return super.formatDoubleToString(total);
	}

	@Transient
	public String getPaidByCreditAmountLabel() {
		return super.formatDoubleToString(this.paidByCreditAmount);
	}

	@Transient
	public String getDeliveryFeeLabel() {
		return super.formatDoubleToString(this.deliveryFee);
	}

	@Transient
	public Boolean getIsTaxable() {
		for (OrderDetail detail : this.orderDetails) {
			if (detail.getIsTaxable()) {
				return Boolean.TRUE;
			}
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanAddRemoveProduct() {
		if (this.status == STATUS.UNKNOWN || this.status == STATUS.RECEIVED) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanSave() {
		if (this.status == STATUS.UNKNOWN) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanConfirm() {
		if (this.status == STATUS.RECEIVED) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanPrepare() {
		if (this.status == STATUS.CONFIRMED) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanDeliver() {
		if (this.status == STATUS.PREPARED) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanPickup() {
		if (this.status == STATUS.DELIVERED) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanUnpickup() {
		if (this.status == STATUS.PICKED_UP) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanPayDirect() {
		if (this.status.compareTo(STATUS.CONFIRMED) >= 0
				&& this.status.compareTo(STATUS.PAID) < 0) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanPay() {
		if (this.status == STATUS.DELIVERED || this.status == STATUS.PICKED_UP) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanUnpay() {
		if (this.status == STATUS.PAID) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanCancel() {
		if (this.status.compareTo(STATUS.PREPARED) < 0) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanEdit() {
		if (this.status == STATUS.RECEIVED) {
			MarketDate marketDate = new MarketDate(this.requiredDate);
			if (marketDate.getIsPasted() == false) {
				return Boolean.TRUE;
			}
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getCanRestore() {
		if (this.status == STATUS.CANCELED) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Transient
	public Boolean getIsReceived() {
		return (this.status == STATUS.RECEIVED ? Boolean.TRUE : Boolean.FALSE);
	}

	@Transient
	public Boolean getIsConfirmed() {
		return (this.status == STATUS.CONFIRMED ? Boolean.TRUE : Boolean.FALSE);
	}

	@Transient
	public Boolean getIsAfterConfirmed() {
		if (this.status == STATUS.RECEIVED || this.status == STATUS.CANCELED) {
			return Boolean.FALSE;
		}
		return Boolean.TRUE;
	}

	@Transient
	public Boolean getIsPrepared() {
		return (this.status == STATUS.PREPARED ? Boolean.TRUE : Boolean.FALSE);
	}

	@Transient
	public Boolean getIsAfterPrepared() {
		if (this.status == STATUS.RECEIVED || this.status == STATUS.CANCELED
				|| this.status == STATUS.CONFIRMED) {
			return Boolean.FALSE;
		}
		return Boolean.TRUE;
	}

	@Transient
	public Boolean getIsDelivered() {
		return (this.status == STATUS.DELIVERED ? Boolean.TRUE : Boolean.FALSE);
	}

	@Transient
	public Boolean getIsPickuped() {
		return (this.status == STATUS.PICKED_UP ? Boolean.TRUE : Boolean.FALSE);
	}

	@Transient
	public Boolean getIsPaid() {
		return (this.status == STATUS.PAID ? Boolean.TRUE : Boolean.FALSE);
	}

	@Transient
	public Boolean getIsCanceled() {
		return (this.status == STATUS.CANCELED ? Boolean.TRUE : Boolean.FALSE);
	}

	@Transient
	public Boolean getIsUnknown() {
		return (this.status == STATUS.UNKNOWN ? Boolean.TRUE : Boolean.FALSE);
	}

	public Boolean getIsHaveFrozenFood() {
		for (OrderDetail detail : this.getOrderDetails()) {
			if (detail.getProduct().getIsFrozenFood()) {
				return Boolean.TRUE;
			}
		}
		return Boolean.FALSE;
	}

	@Transient
	public Integer getPromotionRewardRate() {
		Integer reward = 0;
		for (Promotion promotion : this.promotions) {
			reward = reward + promotion.getRewardRate();

		}
		return reward;
	}

	@Transient
	public String getPromotionRewardRateLabel() {
		Integer reward = this.getPromotionRewardRate();
		return reward + "%";
	}

	public Email getEmail() {
		Locale customerLocale = this.customer.getLanguage().getLocale();
		Email email = new Email();
		StringBuilder text = new StringBuilder();
		Formatter formatter = new Formatter(text);
		String subject = this.getMessage("yourOrder", customerLocale) + ":"
				+ this.number + this.getMessage("hasBeen", customerLocale)
				+ " " + this.status.getLabel(customerLocale);

		text.append(this.getMessage("dearCustomer", customerLocale));
		text.append(" ");
		text.append(this.customer.getLastName());
		text.append(" ");
		text.append(this.customer.getFirstName());
		text.append(":");
		text.append("<br/>");

		text.append(this.getMessage("yourOrder", customerLocale));
		text.append(" : ");
		text.append(this.number);
		text.append(" ");
		text.append(this.getMessage("hasBeen", customerLocale));
		text.append(this.status.getLabel(customerLocale));
		text.append("<br/>");

		text.append(this.getMessage("pickUpLocation", customerLocale));
		text.append(": (");
		text.append(this.customer.getDropOffLocation().getName());
		text.append(") ");
		text.append(this.customer.getDropOffLocation().getAddress());
		text.append("<br/>");

		text.append(this.getMessage("pickUpDateTime", customerLocale));
		text.append(" : ");
		text.append(this.requiredDate.toString());
		text.append(" ");
		text.append(this.pickupTimeRangeLabel);

		text.append("<br/>");
		text.append("<br/>");

		text.append("<table>");
		text.append("<tr>");
		text.append("<td>");
		text.append(this.getMessage("productName", customerLocale));
		text.append("</td>");
		text.append("<td>");
		text.append(this.getMessage("quantity", customerLocale));
		text.append("</td>");
		text.append("<td>");
		text.append(this.getMessage("unit", customerLocale));
		text.append("</td>");
		text.append("<td>");
		text.append(this.getMessage("price", customerLocale));
		text.append("</td>");
		text.append("<td>");
		text.append(this.getMessage("subtotal", customerLocale));
		text.append("</td>");
		text.append("</tr>");

		switch (this.status) {
		case RECEIVED:
		case CANCELED:
			for (OrderDetail detail : this.getOrderDetails()) {
				text.append("<tr>");
				text.append("<td>");
				text.append(detail.getProduct().getNameLabel(customerLocale));
				text.append("</td>");
				text.append("<td>");
				text.append(detail.getOrderedQuantity());
				text.append("</td>");
				text.append("<td>");
				text.append(detail.getProduct().getStockUnitLabel(
						customerLocale));
				text.append("</td>");
				text.append("<td>");
				formatter.format("$%1.2f", detail.getOrderedPrice());
				text.append("/"
						+ detail.getProduct().getPriceUnitLabel(customerLocale));
				if (detail.getIsTaxable()) {
					text.append(this.getMessage("plusTax"));
				}
				text.append("</td>");
				text.append("<td>");
				if (detail.getProduct().getIsCanCaculateExactAmount() == Boolean.FALSE) {
					text.append("≈");
				}
				formatter.format("$%1.2f", detail.getOrderedSubTotal());
				text.append("</td>");
				text.append("</tr>");
			}
			text.append("</table>");
			text.append("<br/>");
			text.append("<br/>");
			text.append(this.getMessage("total", customerLocale) + ":");
			formatter.format("$%1.2f", this.getOrderedTotalAmount());
			if (this.getIsTaxable()) {
				text.append("<br/>");
				text.append(this.getMessage("GST", customerLocale) + ":");
				text.append(this.getOrderedTotalGSTLabel());
				text.append("   ");
				text.append(this.getMessage("QST", customerLocale) + ":");
				text.append(this.getOrderedTotalQSTLabel());
			}

			if (this.getPaidByCreditAmount() > 0D) {
				text.append("<br/>");
				text.append(this.getMessage("paidByCredit", customerLocale)
						+ ": ");
				formatter.format("$%1.2f", this.getPaidByCreditAmount());
			}
			text.append("<br/>");
			
			if (this.getDeliveryFee() > 0.01) {
				text.append(this.getMessage("deliveryFee", customerLocale) + ":");
				text.append(" $" + this.getDeliveryFeeLabel());
			}
			text.append("<br/>");
			
			text.append(this.getMessage("charge", customerLocale) + ":");
			formatter.format("$%1.2f", this.getOrderedTotalCharge());

			break;
		case CONFIRMED:
			for (OrderDetail detail : this.getOrderDetails()) {
				text.append("<tr>");
				text.append("<td>");
				text.append(detail.getProduct().getNameLabel(customerLocale));
				text.append("</td>");
				text.append("<td>");
				text.append(detail.getConfirmedQuantity());
				text.append("</td>");
				text.append("<td>");
				text.append(detail.getProduct().getStockUnitLabel(
						customerLocale));
				text.append("</td>");
				text.append("<td>");
				formatter.format("$%1.2f", detail.getConfirmedPrice());
				text.append("/"
						+ detail.getProduct().getPriceUnitLabel(customerLocale));
				if (detail.getProduct().getIsTaxable()) {
					text.append(this.getMessage("plusTax", customerLocale));
				}
				text.append("</td>");
				text.append("<td>");

				if (detail.getProduct().getIsCanCaculateExactAmount() == Boolean.FALSE) {
					text.append("≈");
				}
				formatter.format("$%1.2f", detail.getConfirmedSubTotal());
				text.append("</td>");
				text.append("</tr>");
			}

			text.append("</table>");
			text.append("<br/>");
			text.append("<br/>");
			text.append(this.getMessage("total", customerLocale) + ":");
			formatter.format("$%1.2f", this.getConfirmedTotalAmount());
			if (this.getIsTaxable()) {
				text.append("<br/>");
				text.append(this.getMessage("GST", customerLocale) + ":");
				text.append(this.getConfirmedTotalGSTLabel());
				text.append("   ");
				text.append(this.getMessage("QST", customerLocale) + ":");
				text.append(this.getConfirmedTotalQSTLabel());
			}

			if (this.getPaidByCreditAmount() > 0D) {
				text.append("<br/>");
				text.append(this.getMessage("paidByCredit", customerLocale)
						+ ": ");
				formatter.format("$%1.2f", this.getPaidByCreditAmount());
			}

			text.append("<br/>");
			if (this.getDeliveryFee() > 0.01) {
				text.append(this.getMessage("deliveryFee", customerLocale) + ":");
				text.append(" $" + this.getDeliveryFeeLabel());
			}
			text.append("<br/>");
			text.append(this.getMessage("charge", customerLocale) + ": ");
			formatter.format("$%1.2f", this.getConfirmedTotalCharge());

			break;
		case PREPARED:
		case DELIVERED:
		case PICKED_UP:
		case PAID:
			for (OrderDetail detail : this.getOrderDetails()) {
				text.append("<tr>");
				text.append("<td>");
				text.append(detail.getProduct().getNameLabel(customerLocale));
				text.append("</td>");
				text.append("<td>");
				text.append(detail.getDeliveredQuantity());
				text.append("</td>");
				text.append("<td>");
				text.append(detail.getProduct().getPriceUnitLabel(
						customerLocale));
				text.append("</td>");
				text.append("<td>");
				formatter.format("$%1.2f", detail.getDeliveredPrice());
				text.append("/"
						+ detail.getProduct().getPriceUnitLabel(customerLocale));
				if (detail.getProduct().getIsTaxable()) {
					text.append(this.getMessage("plusTax", customerLocale));
				}
				text.append("</td>");
				text.append("<td>");

				formatter.format("$%1.2f", detail.getDeliveredSubTotal());
				text.append("</td>");
				text.append("</tr>");
			}

			text.append("</table>");
			text.append("<br/>");
			text.append("<br/>");
			text.append(this.getMessage("total", customerLocale) + ":");
			formatter.format("$%1.2f", this.getDeliveredTotalAmount());
			if (this.getIsTaxable()) {
				text.append("<br/>");
				text.append(this.getMessage("GST", customerLocale) + ":");
				text.append(this.getDeliveredTotalGSTLabel());
				text.append("   ");
				text.append(this.getMessage("QST", customerLocale) + ":");
				text.append(this.getDeliveredTotalQSTLabel());
			}

			if (this.getDiscountAmount() > 0D) {
				text.append("<br/>");
				text.append(this.getMessage("extraDiscount", customerLocale)
						+ ": ");
				formatter.format("$%1.2f", this.getDiscountAmount());
			}

			if (this.getPaidByCreditAmount() > 0D) {
				text.append("<br/>");
				text.append(this.getMessage("paidByCredit", customerLocale)
						+ ": ");
				formatter.format("$%1.2f", this.getPaidByCreditAmount());
			}

			text.append("<br/>");
			if (this.getDeliveryFee() > 0.01) {
				text.append(this.getMessage("deliveryFee", customerLocale) + ":");
				text.append(" $" + this.getDeliveryFeeLabel());
			}
			text.append("<br/>");
			text.append(this.getMessage("charge", customerLocale) + ": ");
			formatter.format("$%1.2f", this.getDeliveredTotalCharge());

			if (this.getPaidAmount() > 0) {
				text.append("<br/>");
				text.append(this.getMessage("paid", customerLocale) + ": ");
				formatter.format("$%1.2f", this.getPaidAmount());
			}
			break;

		}

		text.append("<br/>");
		text.append("<br/>");
		if (this.getPromotionRewardRate() > 0) {
			text.append("<br/>");
			if (this.status == STATUS.PAID) {
				String deposit = this.getMessage("weHaveDepositedCredit",
						customerLocale);
				String depositFormat = MessageFormat.format(deposit,
						this.getDeliveredTotalAmountLabel(),
						this.getPromotionRewardRateLabel(),
						this.getPromotionRewardAmountLabel());
				text.append(depositFormat);
			} else {
				String afterPaymentFormat = MessageFormat.format(
						this.getMessage("afterPayment", customerLocale),
						this.getPromotionRewardRateLabel());
				text.append(afterPaymentFormat);
			}
		}

		text.append("<br/>");
		text.append("<br/>");

		// if(this.getIsHaveFrozenFood()){
		// text.append(this.getMessage("frozenFoodTips", customerLocale));
		// }
		// text.append("<br/>");
		// if(!this.customer.getDropOffLocation().getIsFridgeAvailable()){
		// text.append(this.getMessage("pickupTips", customerLocale));
		// }

		text.append("<br/>");
		text.append("<br/>");
		String thankYou = this.getMessage("thankYouForShoppingOn",
				customerLocale);
		String thankYouFormat = MessageFormat.format(thankYou,
				this.getMessage("webSiteName", customerLocale));
		text.append(thankYouFormat);
		text.append("<br>");
		text.append(this.getMessage("email", customerLocale));
		text.append("service@QuebecFresh.com");
		text.append("<br>");
		text.append(this.getMessage("telephone", customerLocale));
		text.append("(514)699-4508");

		email.setTo(this.customer.getEmail());
		email.setFrom(this.getMessage("ourEmail", customerLocale));
		email.setWebSiteName(super.getMessage("webSiteName", customerLocale));
		email.setSubject(subject);
		email.setContent(text.toString());
		return email;
	}

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id = 0L;
	@Column(length = 50, nullable = false, unique = true)
	private String number;
	@Column(length = 20)
	@Enumerated(EnumType.STRING)
	private STATUS status = STATUS.UNKNOWN;
	@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
	private java.util.Date orderedDate;
	@Column
	@Temporal(TemporalType.DATE)
	private java.util.Date requiredDate;
	@Column
	@Temporal(TemporalType.TIMESTAMP)
	private java.util.Date confirmedDate;
	@Column
	@Temporal(TemporalType.TIMESTAMP)
	private java.util.Date preparedDate;
	@Column
	@Temporal(TemporalType.TIMESTAMP)
	private java.util.Date deliveredDate;
	@Column
	@Temporal(TemporalType.TIMESTAMP)
	private java.util.Date pickedUpDate;
	@Column
	@Temporal(TemporalType.TIMESTAMP)
	private java.util.Date paidDate;
	@Column
	@Temporal(TemporalType.TIMESTAMP)
	private java.util.Date canceledDate;
	@Column
	private String pickupTimeRangeLabel;
	@Column(length = 500)
	private String comments;
	@Column(columnDefinition = "bit(1) default 0")
	private boolean constant = false; // If this is true, we are going to
										// deliver every week before customer
	@Column(columnDefinition = "bit(1) default 0")
	// cancel this option;
	private boolean allOrNothing = false;// If this is true,customer can not
											// accept part of this order;

	@Column(columnDefinition = "Double default 0")
	private Double paidAmount = 0D; // How much the customer has paid for this
									// order.

	@Column(columnDefinition = "Double default 0")
	private Double discountAmount = 0D; // How much we exempt our customer for
										// this
										// order;
	@Column(columnDefinition = "Double default 0")
	private Double paidByCreditAmount = 0D; // How much we this order was paid
											// by credit;
											//
	@Column(columnDefinition = "Double default 0")
	private Double deliveryFee = 0D;

	// @Column
	// private Integer promotionDiscount = 0; // The discount rate is
	// percentage,
	// // eg 3% OFF, input 3.
	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn(name = "user_id", nullable = false)
	private User customer;

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
	private List<OrderDetail> orderDetails = new ArrayList<OrderDetail>();

	@ManyToMany(fetch = FetchType.LAZY)
	private List<Promotion> promotions = new ArrayList<Promotion>();

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number;
	}

	public STATUS getStatus() {
		return status;
	}

	public void setStatus(STATUS status) {
		this.status = status;
	}

	public java.util.Date getOrderedDate() {
		return orderedDate;
	}

	public void setOrderedDate(java.util.Date orderedDate) {
		this.orderedDate = orderedDate;
	}

	public java.util.Date getRequiredDate() {
		return requiredDate;
	}

	public void setRequiredDate(java.util.Date requiredDate) {
		this.requiredDate = requiredDate;
	}

	public java.util.Date getConfirmedDate() {
		return confirmedDate;
	}

	public void setConfirmedDate(java.util.Date confirmedDate) {
		this.confirmedDate = confirmedDate;
	}

	public java.util.Date getPreparedDate() {
		return preparedDate;
	}

	public void setPreparedDate(java.util.Date preparedDate) {
		this.preparedDate = preparedDate;
	}

	public java.util.Date getDeliveredDate() {
		return deliveredDate;
	}

	public void setDeliveredDate(java.util.Date deliveredDate) {
		this.deliveredDate = deliveredDate;
	}

	public java.util.Date getPickedUpDate() {
		return pickedUpDate;
	}

	public void setPickedUpDate(java.util.Date pickedUpDate) {
		this.pickedUpDate = pickedUpDate;
	}

	public java.util.Date getPaidDate() {
		return paidDate;
	}

	public void setPaidDate(java.util.Date paidDate) {
		this.paidDate = paidDate;
	}

	public java.util.Date getCanceledDate() {
		return canceledDate;
	}

	public void setCanceledDate(java.util.Date canceledDate) {
		this.canceledDate = canceledDate;
	}

	public String getPickupTimeRangeLabel() {
		return pickupTimeRangeLabel;
	}

	public void setPickupTimeRangeLabel(String pickupTimeRangeLabel) {
		this.pickupTimeRangeLabel = pickupTimeRangeLabel;
	}

	public String getComments() {
		return comments;
	}

	public void setComments(String comments) {
		this.comments = comments;
	}

	public boolean isConstant() {
		return constant;
	}

	public void setConstant(boolean constant) {
		this.constant = constant;
	}

	public boolean isAllOrNothing() {
		return allOrNothing;
	}

	public void setAllOrNothing(boolean allOrNothing) {
		this.allOrNothing = allOrNothing;
	}

	public Double getPaidAmount() {
		return paidAmount;
	}

	public void setPaidAmount(Double paidAmount) {
		this.paidAmount = paidAmount;
	}

	public Double getDiscountAmount() {
		return discountAmount;
	}

	public void setDiscountAmount(Double discountAmount) {
		this.discountAmount = discountAmount;
	}

	public Double getPaidByCreditAmount() {
		return paidByCreditAmount;
	}

	public void setPaidByCreditAmount(Double paidByCreditAmount) {
		this.paidByCreditAmount = paidByCreditAmount;
	}

	public Double getDeliveryFee() {
		return deliveryFee;
	}

	public void setDeliveryFee(Double deliveryFee) {
		this.deliveryFee = deliveryFee;
	}

	public User getCustomer() {
		return customer;
	}

	public void setCustomer(User customer) {
		this.customer = customer;
	}

	public List<OrderDetail> getOrderDetails() {
		return orderDetails;
	}

	public void setOrderDetails(List<OrderDetail> orderDetails) {
		this.orderDetails = orderDetails;
	}

	public List<Promotion> getPromotions() {
		return promotions;
	}

	public void setPromotions(List<Promotion> promotions) {
		this.promotions = promotions;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (allOrNothing ? 1231 : 1237);
		result = prime * result
				+ ((canceledDate == null) ? 0 : canceledDate.hashCode());
		result = prime * result
				+ ((comments == null) ? 0 : comments.hashCode());
		result = prime * result
				+ ((confirmedDate == null) ? 0 : confirmedDate.hashCode());
		result = prime * result + (constant ? 1231 : 1237);
		result = prime * result
				+ ((deliveredDate == null) ? 0 : deliveredDate.hashCode());
		result = prime * result
				+ ((discountAmount == null) ? 0 : discountAmount.hashCode());
		result = prime * result + ((number == null) ? 0 : number.hashCode());
		result = prime * result
				+ ((orderedDate == null) ? 0 : orderedDate.hashCode());
		result = prime * result
				+ ((paidAmount == null) ? 0 : paidAmount.hashCode());
		result = prime * result
				+ ((paidDate == null) ? 0 : paidDate.hashCode());
		result = prime * result
				+ ((pickedUpDate == null) ? 0 : pickedUpDate.hashCode());
		result = prime * result
				+ ((preparedDate == null) ? 0 : preparedDate.hashCode());
		result = prime * result
				+ ((requiredDate == null) ? 0 : requiredDate.hashCode());
		result = prime * result + ((status == null) ? 0 : status.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Order other = (Order) obj;
		if (allOrNothing != other.allOrNothing)
			return false;
		if (canceledDate == null) {
			if (other.canceledDate != null)
				return false;
		} else if (!canceledDate.equals(other.canceledDate))
			return false;
		if (comments == null) {
			if (other.comments != null)
				return false;
		} else if (!comments.equals(other.comments))
			return false;
		if (confirmedDate == null) {
			if (other.confirmedDate != null)
				return false;
		} else if (!confirmedDate.equals(other.confirmedDate))
			return false;
		if (constant != other.constant)
			return false;
		if (deliveredDate == null) {
			if (other.deliveredDate != null)
				return false;
		} else if (!deliveredDate.equals(other.deliveredDate))
			return false;
		if (discountAmount == null) {
			if (other.discountAmount != null)
				return false;
		} else if (!discountAmount.equals(other.discountAmount))
			return false;
		if (number == null) {
			if (other.number != null)
				return false;
		} else if (!number.equals(other.number))
			return false;
		if (orderedDate == null) {
			if (other.orderedDate != null)
				return false;
		} else if (!orderedDate.equals(other.orderedDate))
			return false;
		if (paidAmount == null) {
			if (other.paidAmount != null)
				return false;
		} else if (!paidAmount.equals(other.paidAmount))
			return false;
		if (paidDate == null) {
			if (other.paidDate != null)
				return false;
		} else if (!paidDate.equals(other.paidDate))
			return false;
		if (pickedUpDate == null) {
			if (other.pickedUpDate != null)
				return false;
		} else if (!pickedUpDate.equals(other.pickedUpDate))
			return false;
		if (preparedDate == null) {
			if (other.preparedDate != null)
				return false;
		} else if (!preparedDate.equals(other.preparedDate))
			return false;
		if (requiredDate == null) {
			if (other.requiredDate != null)
				return false;
		} else if (!requiredDate.equals(other.requiredDate))
			return false;
		if (status != other.status)
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Order [id=" + id + ", number=" + number + ", status=" + status
				+ ", orderedDate=" + orderedDate + ", requiredDate="
				+ requiredDate + ", confirmedDate=" + confirmedDate
				+ ", preparedDate=" + preparedDate + ", deliveredDate="
				+ deliveredDate + ", pickedUpDate=" + pickedUpDate
				+ ", paidDate=" + paidDate + ", canceledDate=" + canceledDate
				+ ", comments=" + comments + ", constant=" + constant
				+ ", allOrNothing=" + allOrNothing + ", paidAmount="
				+ paidAmount + ", discountAmount=" + discountAmount
				+ ", customer=" + customer + "]";
	}

}
